// Code from http://progrnotes.blogspot.com/2010/09/c-android.html
// Modified not to be private class of a activity and added some debug logging

package se.peterbjorkman.android.trafikkamera;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.os.AsyncTask;
import android.util.Log;

public class DownloadImageTask extends AsyncTask<String, DownloadImageTask.StatusData, DownloadImageTask.ResultData> {
	public static final String LOGTAG = DownloadImageTask.class.getName();
	private Context mContext;
	private Drawable d;
	private HttpURLConnection conn;
	private InputStream stream; // to read
	private ByteArrayOutputStream out; // to write

	private int fileSize;
	private int downloaded; // number of bytes downloaded
	private int status = DOWNLOADING; // status of current process
	private int mLastDone = -1;
	private int mLastTotal = -1;
	private int mLastStatus = -1;
	private Callback mCallback;

	private static final int MAX_BUFFER_SIZE = 1024; // 1kb

	public static final int STARTING = 0;
	public static final int CONNECTING = 1;
	public static final int DOWNLOADING = 2;
	public static final int PROCESSING = 3;
	public static final int COMPLETE = 4;

	public DownloadImageTask(Context context, Callback callback) {
		d = null;
		conn = null;
		fileSize = 0;
		downloaded = 0;
		status = STARTING;
		mContext = context;
		mCallback = callback;
	}

	public boolean isOnline() {
		try {
			ConnectivityManager cm = (ConnectivityManager) mContext
					.getSystemService(Context.CONNECTIVITY_SERVICE);
			return cm.getActiveNetworkInfo().isConnectedOrConnecting();
		} catch (Exception e) {
			return false;
		}
	}

	@Override
	protected ResultData doInBackground(String... url) {
		Log.v(LOGTAG, "doInBackground");
		try {
			if ( /*isOnline() == */true) {  // TODO Check online
				status = CONNECTING;
				Log.v(LOGTAG, "Starting loading image by URL: " + url[0]);
				conn = (HttpURLConnection) new URL(url[0]).openConnection();
				conn.connect();
				status = DOWNLOADING;
				Log.v(LOGTAG, "connection opened: ");
				fileSize = conn.getContentLength();
				if (fileSize < 0){
					Log.w(LOGTAG, "No contentlength recieved from url: " + url[0]);
					fileSize = 0;
					out = new ByteArrayOutputStream();
				} else {
					Log.v(LOGTAG, "contentlength recieved: " + fileSize);
					out = new ByteArrayOutputStream(fileSize);	
				}

				int statusCode = conn.getResponseCode();
				Log.v(LOGTAG, "statuscode: " + statusCode);
				// Make sure response code is in the 200 range.
				if (statusCode / 100 != 2) {
					Log.e(LOGTAG, "statuscode: " + statusCode);
					return null; // TODO Exception with message
				}

				stream = conn.getInputStream();
				// loop with step 1kb
				while (status == DOWNLOADING) {
					byte buffer[];

					int bufferSize;
					if (fileSize <= 0 || fileSize - downloaded > MAX_BUFFER_SIZE) {
						bufferSize = MAX_BUFFER_SIZE;
					} else {
						bufferSize = (int) (fileSize - downloaded);
					}

					buffer = new byte[bufferSize];

					Log.v(LOGTAG, "Reading " +  bufferSize + " more bytes");
					int read = stream.read(buffer);

					if (read == -1) {
						Log.v(LOGTAG, "Completed");
						status = PROCESSING;
						publishProgress(downloaded, downloaded, status);
						break;
					}
					// writing to buffer
					out.write(buffer, 0, read);
					downloaded += read;
					publishProgress(downloaded, fileSize, status);
				}

				long lastModified = conn.getLastModified();
				Date test = new Date(lastModified);
				if (status == DOWNLOADING || status == PROCESSING) {
					status = COMPLETE;
				}
				publishProgress(0, 0, status);
				Log.v(LOGTAG, "Createing drawable");
				d = Drawable.createFromStream(
						(InputStream) new ByteArrayInputStream(out
								.toByteArray()), "filename");

				Log.v(LOGTAG, "Drawable created");
				ResultData data = new ResultData();
				data.drawable = (BitmapDrawable)d;
				data.modifiedDate = test;
				return data;

			}
			else {
				return null;
			}
		} catch (Exception e) {
			ResultData data = new ResultData();
			data.drawable = null;
			data.errorMessage = "Fel vid nedladdning av " + url[0] + "\n" + e.getLocalizedMessage();
			return data;
		}
	}
	
	private void publishProgress(int done, int total, int status){
		if (!shouldReportStatus(done,total,status))
			return;
		mLastDone = done;
		mLastTotal = total;
		mLastStatus = status;
		StatusData data = new StatusData();
		data.done = done;
		data.total = total;
		data.status = status;
		publishProgress(data);
	}

	private boolean shouldReportStatus(int done, int total, int status2) {
		if (mLastDone < 0)
			return true;
		if (mLastTotal != total)
			return true;
		if (mLastStatus != status)
			return true;
		if (done - mLastDone > 2500)
			return true;
		
		return false;
	}

	@Override
	protected void onProgressUpdate(StatusData... changed) {
		Log.v(LOGTAG, "We are making progress.");
		mCallback.progressCallback(changed[0].done, changed[0].total);

		String status = "Arbetar";
		switch (changed[0].status) {
		case CONNECTING:
			status = "Ansluter";
			break;
		case DOWNLOADING:
			status = "Tar emot data";
			break;
		case PROCESSING:
			status = "Behandlar data";
			break;
		case COMPLETE:
			status = "Avslutar";
			break;
		}
		mCallback.statusCallback(status);
	}

	@Override
	protected void onPreExecute() {
		Log.v(LOGTAG, "onPreExecute.");
		mCallback.statusCallback("Starting...");
	}

	@Override
	protected void onPostExecute(ResultData result) {
		if (result.drawable != null) {
			mCallback.imageCallback(result.drawable);
			mCallback.modifiedTimeCallback(result.modifiedDate);
		} else {
			AlertDialog alertDialog;
			alertDialog = new AlertDialog.Builder(mContext).create();
			alertDialog.setTitle("Fel");
			alertDialog.setMessage(result.errorMessage);
			alertDialog.setButton("Close",
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dlg, int sum) {
							// do nothing, close
						}
					});
			alertDialog.show();
		}
	}
	
	public interface Callback{
		void imageCallback(BitmapDrawable drawable);
		void modifiedTimeCallback(Date date);
		void progressCallback(int done, int total);
		void statusCallback(String status);
	}

	class ResultData{
		public BitmapDrawable drawable;
		public Date modifiedDate;
		public String errorMessage;
	}
	
	class StatusData{
		public int status = -1;
		public int done = -1 ;
		public int total = -1;
	}
}
